/**
 * 
 */
package com.celephais.common.client.ui;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.Widget;

/**
 * @author Eugene Shen
 * 
 * This manages a collection of buttons in a way that's very similar to a TDI's
 * tab headings.  Basically, when a button is pressed, all other buttons will
 * pop up and the associated TDIButtonSetAction's onClick will be called to
 * notify the listener of the change.
 * 
 * TODO: Crap.  Didn't need this -- should have just used a TabBar.
 */
public class TDIButtonSet implements ClickHandler {
	
	private LinkedList<Widget> widgets;
	private LinkedList<ToggleButton> buttons;
	private HashMap<Widget, HandlerRegistration> handlerRegs;
	private boolean includeRemoveBtn;
	private RemoveBtnClickHandler removeBtnClickHandler;
	private TDIButtonSetAction callback;

	/**
	 * Default constructor.  Sets callback at the same time.
	 * @param callback The TDIButtonSetAction callback to use.
	 * @param includeRemoveButton Whether or not to include a remove button to
	 *        the generated widget.
	 */
	public TDIButtonSet(TDIButtonSetAction callback, boolean includeRemoveButton) {
		super();
		widgets = new LinkedList<Widget>();
		buttons = new LinkedList<ToggleButton>();
		handlerRegs = new HashMap<Widget, HandlerRegistration>();
		this.callback = callback;
		this.includeRemoveBtn = includeRemoveButton;
		if (includeRemoveBtn) {
			removeBtnClickHandler = new RemoveBtnClickHandler();
		}
	}
	
	/**
	 * Sets the TDIButtonSetAction callback.
	 * @param callback The callback to call when a button is pressed.
	 */
	public void setCallback(TDIButtonSetAction callback) {
		this.callback = callback;
	}
	
	/**
	 * Adds a button with the given text.
	 * @param btnText The text of the new ToggleButton.
	 * @return The position the new button was added at.  -1 if the add failed.
	 */
	public int addButton(String btnText) {
		ToggleButton btn = new ToggleButton(btnText);
		Widget widget = createWidget(btn);
		if (widgets.add(widget)) {
			if (buttons.add(btn)) {
				handlerRegs.put(widget, btn.addClickHandler(this));
				return widgets.size() - 1;
			}
			else {
				widgets.remove(widget);
				return -1;
			}
		}
		else {
			return -1;
		}
	}
	
	/**
	 * Inserts a button at the specified position.
	 * @param btnText The text of the new ToggleButton.
	 * @param position The 0-based index location to insert the button.
	 * @return Whether or not the insert was successful (will return false if
	 *         the button already exists or if the index is out of bounds).
	 */
	public boolean insertButton(String btnText, int position) {
		ToggleButton btn = new ToggleButton(btnText);
		Widget widget = createWidget(btn);
		try {
			widgets.add(position, widget);
			buttons.add(position, btn);
		}
		catch (IndexOutOfBoundsException e) {
			return false;
		}
		handlerRegs.put(widget, btn.addClickHandler(this));
		return true;
	}
	
	/**
	 * Removes a button.
	 * @param btn The button to remove.
	 * @return Whether or not the button was successfully removed.
	 */
	public boolean removeButton(int index) {
		Widget wid = null;
		ToggleButton btn = null;
		try {
			wid = widgets.remove(index);
			btn = buttons.remove(index);
		}
		catch (IndexOutOfBoundsException e) {
			return false;
		}
		
		if (wid != null && btn != null) {
			HandlerRegistration reg = handlerRegs.get(wid);
			if (reg != null) {
				reg.removeHandler();
			}
			
			if (callback != null) {
				callback.onRemove(index, wid);
			}
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * Gets the number of buttons currently being managed.
	 * @return The number of buttons currently being managed.
	 */
	public int getWidgetCount() {
		return widgets.size();
	}
	
	/**
	 * Gets the generated widgets that represent the tab controls.
	 * @return The list of generated widgets.
	 */
	public List<Widget> getWidgets() {
		return widgets;
	}
	
	/**
	 * This will create a Widget based on the ToggleButton.
	 * If you ever need to create a similar class that simply differs in the
	 * UI, then just subclass and override this method.
	 * @param btn The button to create a Widget from. [sic]
	 * @return The Widget created from the button.
	 */
	protected Widget createWidget(ToggleButton btn) {
		HorizontalPanel hp = new HorizontalPanel();
		hp.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
		hp.add(btn);
		if (includeRemoveBtn) {
			Button removeBtn = new Button("X");
			removeBtn.setTitle("Remove");
			removeBtn.addClickHandler(removeBtnClickHandler);
			hp.add(removeBtn);
		}
		return hp;
	}
	
	/**
	 * Override this method if you have a different click-handling scheme.	
	 */
	@Override
	public void onClick(ClickEvent event) {
		Object source = event.getSource();
		// Sanity check...
		if (source instanceof ToggleButton) {
			ToggleButton btn = (ToggleButton)source;
			int idx = buttons.indexOf(btn);
			if (idx != -1) {
				for (ToggleButton otherBtn : buttons) {
					if (otherBtn != btn) {
						otherBtn.setDown(false);
					}
				}
				btn.setDown(true);
				if (callback != null) {
					callback.onClick(idx, widgets.get(idx));
				}
			}
			else {
				// WTF?
			}
		}
		else {
			// WTF?
		}
	}
	
	private class RemoveBtnClickHandler implements ClickHandler {
		@Override
		public void onClick(ClickEvent event) {
			Object source = event.getSource();
			if (source instanceof Button) {
				Button removeBtn = (Button)source;
				Widget parent = removeBtn.getParent();
				int parentIdx = widgets.indexOf(parent);
				if (parentIdx != -1) {
					removeButton(parentIdx);
				}
				else {
					// WTF?  Ignore...
				}
			}
			else {
				// WTF?  Ignore...
			}
		}
	}
}
